import * as THREE from "three";
import { PointerLockControls } from "three/examples/jsm/controls/PointerLockControls";

/*
 * @Author: hongbin
 * @Date: 2022-06-13 19:40:04
 * @LastEditors: hongbin
 * @LastEditTime: 2022-06-21 16:41:14
 * @Description: 适用于本项目的助手函数
 */
export type positionAxis = "x" | "y" | "z";

const axisArr: positionAxis[] = ["x", "y", "z"];
/**
 * 随机从 x,y,z轴中选一个方向
 */
export function randomAxis(): positionAxis {
    return axisArr[Math.floor(Math.random() * 3)];
    // return 'x';
}

/**
 * @description: 对象的某属性 逐帧变换
 * @param {*} mash 独享
 * @param {*} prop 属性
 * @param {*} valRegion 变化区间
 * @param {*} paragraph 分多少段完成变换
 * @param {*} onComplete 执行完回掉函数
 * @return {*}
 */
export const animationFrameTrans = (
    mash: THREE.Material,
    prop: Omit<keyof THREE.Material, "isMaterial">,
    valRegion = [0, 1],
    paragraph = 10,
    onComplete: () => any = () => {}
) => {
    const diff = valRegion[1] - valRegion[0];
    const count = diff / paragraph;
    //@ts-ignore
    mash[prop] = valRegion[0];

    const tick = () => {
        //@ts-ignore
        mash[prop] += count;
        //@ts-ignore
        if (mash[prop] <= valRegion[1]) {
            requestAnimationFrame(tick);
        } else onComplete && onComplete();
    };
    tick();
};

export type Intersection = THREE.Intersection<THREE.Object3D<THREE.Event>>[];

/**
 * @description:  传入角度即方向 判断附近有没有障碍物 返回是否有碰撞或返回障碍物
 * @param {controls} controls  控制器
 * @param {objects} objects  碰撞检测的对象
 * @param {eyeHeight} eyeHeight  眼睛的高度 决定是脚底或是头部或事腰部 任意高度
 * @param {angle} angle  射线旋转角度 决定前后左右
 * @param {far} far  检测的距离
 * @return  Object3D[]
 */
export const collideCheck = (
    controls: PointerLockControls,
    objects: THREE.Object3D[],
    eyeHeight: number,
    angle: number,
    far: number = 5
) => {
    let rotationMatrix = new THREE.Matrix4();
    rotationMatrix.makeRotationY((angle * Math.PI) / 180);
    //返回摄像机的观看方向
    const cameraDirection = controls.getDirection(new THREE.Vector3(0, 0, 0)).clone();
    //将该向量乘以四阶矩阵m（第四个维度隐式地为1），并按角度进行划分。
    cameraDirection.applyMatrix4(rotationMatrix);

    const raycaster = new THREE.Raycaster(
        controls.getObject().position.clone(),
        cameraDirection,
        0,
        far
    );
    raycaster.ray.origin.y -= eyeHeight;
    const intersections = raycaster.intersectObjects(objects, false);
    // intersections.length && console.log(intersections[0].object.userData.index);
    return intersections;
};

// const around = {
//     forward: { axis: "z", vector: 1, angle: 0 },
//     backward: { axis: "z", vector: -1, angle: 180 },
//     left: { axis: "x", vector: -1, angle: 90 },
//     right: { axis: "x", vector: 1, angle: 270 },
// };

// controls: PointerLockControls,
// objects: THREE.Object3D[],
// eyeHeight: number

/**
 * @description: 检测四周有没有被碰撞--区别于按下判断一次对应一侧 只是贴身的碰撞被推着走
 * @return {*}
 */
export function checkAllAroundCollide(
    collideCollideBlocks: Set<number>,
    ...rest: [PointerLockControls, THREE.Object3D[], number]
) {
    for (let angle = 0; angle <= 3; angle++) {
        const object = collideCheck(...rest, angle * 90, 4);
        object.length && collideCollideBlocks.add(object[0].object.userData.index);
    }
}

export const randomColor = () =>
    `#${Math.floor(Math.random() * 0xffffff)
        .toString(16)
        .padEnd(6, (Math.random() * 10).toString(16)[0])}`;

interface IFacetedBox {
    (
        w: number,
        h: number,
        d: number,
        f: number,
        isWireframed: boolean
    ): THREE.BufferGeometry;
}

/**
 * @description: 有倾斜面面的盒子
 * @param {number} w 长
 * @param {number} h 宽
 * @param {number} d 高
 * @param {number} f 几面
 * @param {boolean} isWireframed 线框(骨架)
 * @return {*}
 */
export const facetedBox: IFacetedBox = (w, h, d, f, isWireframed) => {
    let hw = w * 0.5,
        hh = h * 0.5,
        hd = d * 0.5;
    let vertices = [
        // px
        hw,
        hh - f,
        -hd + f, // 0
        hw,
        -hh + f,
        -hd + f, // 1
        hw,
        -hh + f,
        hd - f, // 2
        hw,
        hh - f,
        hd - f, // 3

        // pz
        hw - f,
        hh - f,
        hd, // 4
        hw - f,
        -hh + f,
        hd, // 5
        -hw + f,
        -hh + f,
        hd, // 6
        -hw + f,
        hh - f,
        hd, // 7

        // nx
        -hw,
        hh - f,
        hd - f, // 8
        -hw,
        -hh + f,
        hd - f, // 9
        -hw,
        -hh + f,
        -hd + f, // 10
        -hw,
        hh - f,
        -hd + f, // 11

        // nz
        -hw + f,
        hh - f,
        -hd, // 12
        -hw + f,
        -hh + f,
        -hd, // 13
        hw - f,
        -hh + f,
        -hd, // 14
        hw - f,
        hh - f,
        -hd, // 15

        // py
        hw - f,
        hh,
        -hd + f, // 16
        hw - f,
        hh,
        hd - f, // 17
        -hw + f,
        hh,
        hd - f, // 18
        -hw + f,
        hh,
        -hd + f, // 19

        // ny
        hw - f,
        -hh,
        -hd + f, // 20
        hw - f,
        -hh,
        hd - f, // 21
        -hw + f,
        -hh,
        hd - f, // 22
        -hw + f,
        -hh,
        -hd + f, // 23
    ];

    let indices = [
        0, 2, 1, 3, 2, 0, 4, 6, 5, 7, 6, 4, 8, 10, 9, 11, 10, 8, 12, 14, 13, 15, 14, 12,
        16, 18, 17, 19, 18, 16, 20, 21, 22, 23, 20, 22,

        // link the sides
        3, 5, 2, 4, 5, 3, 7, 9, 6, 8, 9, 7, 11, 13, 10, 12, 13, 11, 15, 1, 14, 0, 1, 15,

        // link the lids
        // top
        16, 3, 0, 17, 3, 16, 17, 7, 4, 18, 7, 17, 18, 11, 8, 19, 11, 18, 19, 15, 12, 16,
        15, 19,
        // bottom
        1, 21, 20, 2, 21, 1, 5, 22, 21, 6, 22, 5, 9, 23, 22, 10, 23, 9, 13, 20, 23, 14,
        20, 13,

        // corners
        // top
        3, 17, 4, 7, 18, 8, 11, 19, 12, 15, 16, 0,
        // bottom
        2, 5, 21, 6, 9, 22, 10, 13, 23, 14, 1, 20,
    ];

    let indicesWire = [
        0,
        1,
        1,
        2,
        2,
        3,
        3,
        0,
        4,
        5,
        5,
        6,
        6,
        7,
        7,
        4,
        8,
        9,
        9,
        10,
        10,
        11,
        11,
        8,
        12,
        13,
        13,
        14,
        14,
        15,
        15,
        12,
        16,
        17,
        17,
        18,
        18,
        19,
        19,
        16,
        20,
        21,
        21,
        22,
        22,
        23,
        23,
        20,
        // link the sides
        2,
        5,
        3,
        4, //px - pz
        6,
        9,
        7,
        8, // pz - nx
        10,
        13,
        11,
        12, // nx - nz
        15,
        0,
        14,
        1, // nz - px

        // link the lids
        // top
        16,
        0,
        17,
        3, // px
        17,
        4,
        18,
        7, // pz
        18,
        8,
        19,
        11, // nx
        19,
        12,
        16,
        15, // nz
        // bottom
        20,
        1,
        21,
        2,
        21,
        5,
        22,
        6,
        22,
        9,
        23,
        10,
        23,
        13,
        20,
        14,
    ];

    let geom = new THREE.BufferGeometry();
    geom.setAttribute(
        "position",
        new THREE.BufferAttribute(new Float32Array(vertices), 3)
    );
    geom.setAttribute("uv", new THREE.BufferAttribute(new Float32Array(vertices), 3));
    geom.setIndex(isWireframed ? indicesWire : indices);
    if (!isWireframed) geom.computeVertexNormals();
    return geom;
};

export function extrudeGeometry(length: number, width: number) {
    // const length = 12, width = 8;
    const shape = new THREE.Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, width);
    shape.lineTo(length, width);
    shape.lineTo(length, 0);
    shape.lineTo(0, 0);

    const extrudeSettings = {
        steps: 2,
        depth: 16,
        bevelEnabled: true,
        bevelThickness: 1,
        bevelSize: 1,
        bevelOffset: 0,
        bevelSegments: 1,
    };

    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
    return geometry;
}
